// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

class ZenFolder extends MozTabbrowserTabGroup {
  #initialized = false;

  static markup = `
      <hbox class="tab-group-label-container" pack="center">
        <html:div class="tab-group-folder-icon"/>
        <label class="tab-group-label" role="button"/>
        <image class="tab-reset-button reset-icon" role="button" keyNav="false" data-l10n-id="zen-folders-unload-all-tooltip"/>
      </hbox>
      <html:div class="tab-group-container">
        <html:div class="zen-tab-group-start" />
      </html:div>
      <vbox class="tab-group-overflow-count-container" pack="center">
        <label class="tab-group-overflow-count" role="button" />
      </vbox>
    `;

  static rawIcon = new DOMParser().parseFromString(
    `
      <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient gradientUnits="userSpaceOnUse" x1="14" y1="5.625" x2="14" y2="22.375" id="gradient-0">
            <stop offset="0" style="stop-color: rgb(255, 255, 255)"/>
            <stop offset="1" style="stop-color: rgb(0% 0% 0%)"/>
          </linearGradient>
          <linearGradient gradientUnits="userSpaceOnUse" x1="14" y1="9.625" x2="14" y2="22.375" id="gradient-1">
            <stop offset="0" style="stop-color: rgb(255, 255, 255)"/>
            <stop offset="1" style="stop-color: rgb(0% 0% 0%)"/>
          </linearGradient>
        </defs>
        <!--Back Folder (path)-->
        <path class="back" d="M8 5.625H11.9473C12.4866 5.625 13.0105 5.80861 13.4316 6.14551L14.2881 6.83105C14.9308 7.34508 15.7298 7.625 16.5527 7.625H20C21.3117 7.625 22.375 8.68832 22.375 10V20C22.375 21.3117 21.3117 22.375 20 22.375H8C6.68832 22.375 5.625 21.3117 5.625 20V8C5.625 6.68832 6.68832 5.625 8 5.625Z" style="fill: var(--zen-folder-behind-bgcolor);">
        </path>
        <path class="back" d="M8 5.625H11.9473C12.4866 5.625 13.0105 5.80861 13.4316 6.14551L14.2881 6.83105C14.9308 7.34508 15.7298 7.625 16.5527 7.625H20C21.3117 7.625 22.375 8.68832 22.375 10V20C22.375 21.3117 21.3117 22.375 20 22.375H8C6.68832 22.375 5.625 21.3117 5.625 20V8C5.625 6.68832 6.68832 5.625 8 5.625Z" style="stroke-width: 1.5px; stroke: var(--zen-folder-stroke); fill: url(#gradient-0); fill-opacity: 0.1;">
        </path>
        <!--Front Folder (rect)-->
        <rect class="front" x="5.625" y="9.625" width="16.75" height="12.75" rx="2.375" style="fill: var(--zen-folder-front-bgcolor);">
        </rect>
        <rect class="front" x="5.625" y="9.625" width="16.75" height="12.75" rx="2.375" style="stroke-width: 1.5px; stroke: var(--zen-folder-stroke); fill: url(#gradient-1); fill-opacity: 0.1;">
        </rect>
        <!--Icon (g)-->
        <g class="icon" style="fill: var(--zen-folder-stroke);">
          <image href="" height="10" width="10"/>
        </g>
        <!--End Icon (g)-->
        <g class="dots" style="fill: var(--zen-folder-stroke);">
          <ellipse cx="10" cy="16" rx="1.25" ry="1.25"/>
          <ellipse cx="14" cy="16" rx="1.25" ry="1.25"/>
          <ellipse cx="18" cy="16" rx="1.25" ry="1.25"/>
        </g>
      </svg>`,
    'image/svg+xml'
  ).documentElement;

  constructor() {
    super();
  }

  connectedCallback() {
    super.connectedCallback();
    this.labelElement.pinned = true;
    if (this.#initialized) {
      return;
    }
    this.#initialized = true;
    this._activeTabs = [];
    this.icon.appendChild(ZenFolder.rawIcon.cloneNode(true));

    this.labelElement.parentElement.setAttribute('context', 'zenFolderActions');

    this.labelElement.onRenameFinished = (newLabel) => {
      this.name = newLabel.trim() || 'Folder';
      const event = new CustomEvent('ZenFolderRenamed', {
        bubbles: true,
      });
      this.dispatchEvent(event);
    };

    if (this.collapsed) {
      this.querySelector('.tab-group-container').setAttribute('hidden', true);
    }
  }

  get icon() {
    return this.querySelector('.tab-group-folder-icon');
  }

  /**
   * Returns the group this folder belongs to.
   * @returns {MozTabbrowserTabGroup|null} The group this folder belongs to, or null if it is not part of a group.
   **/
  get group() {
    if (gBrowser.isTabGroup(this.parentElement?.parentElement)) {
      return this.parentElement.parentElement;
    }
    return null;
  }

  get isZenFolder() {
    return true;
  }

  get activeGroups() {
    let activeGroups = [];
    let currentGroup = this;
    if (currentGroup?.hasAttribute('has-active')) activeGroups.push(currentGroup);
    while (currentGroup?.group) {
      currentGroup = currentGroup?.group;
      if (currentGroup?.hasAttribute('has-active')) {
        activeGroups.push(currentGroup);
      }
    }
    return activeGroups;
  }

  get childActiveGroups() {
    return Array.from(this.querySelectorAll('zen-folder[has-active]'));
  }

  rename() {
    if (!document.documentElement.hasAttribute('zen-sidebar-expanded')) {
      return;
    }
    gZenVerticalTabsManager.renameTabStart({
      target: this.labelElement,
      explicit: true,
    });
  }

  createSubfolder() {
    // We need to expand all parent folders
    let currentFolder = this;
    do {
      currentFolder.collapsed = false;
      currentFolder = currentFolder.group;
    } while (currentFolder);
    gZenFolders.createFolder([], {
      renameFolder: !gZenUIManager.testingEnabled,
      label: 'Subfolder',
      insertAfter: this.querySelector('.tab-group-container').lastElementChild,
    });
  }

  async unpackTabs() {
    this.collapsed = false;
    for (let tab of this.allItems.reverse()) {
      tab = tab.group.hasAttribute('split-view-group') ? tab.group : tab;
      if (tab.hasAttribute('zen-empty-tab')) {
        await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
        gBrowser.removeTab(tab);
      } else {
        gBrowser.ungroupTab(tab);
      }
    }
  }

  async delete() {
    for (const tab of this.allItemsRecursive) {
      await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
      if (tab.hasAttribute('zen-empty-tab')) {
        // Manually remove the empty tabs as removeTabs() inside removeTabGroup
        // does ignore them.
        gBrowser.removeTab(tab);
      }
    }
    await gBrowser.removeTabGroup(this, { isUserTriggered: true });
  }

  get allItemsRecursive() {
    const items = [];
    for (const item of this.allItems) {
      if (item.isZenFolder) {
        items.push(item, ...item.allItemsRecursive);
      } else {
        items.push(item);
      }
    }
    return items;
  }

  get allItems() {
    return [...this.querySelector('.tab-group-container').children].filter(
      (child) => !child.classList.contains('zen-tab-group-start')
    );
  }

  get pinned() {
    return this.isZenFolder;
  }

  /**
   * Intentionally ignore attempts to change the pinned state.
   * ZenFolder instances determine their "pinned" status based on their type (isZenFolder)
   * and do not support being pinned or unpinned via this setter.
   * This no-op setter ensures compatibility with interfaces expecting a pinned property,
   * while preserving the invariant that ZenFolders cannot have their pinned state changed externally.
   */
  set pinned(value) {}

  get iconURL() {
    return this.icon.querySelector('image')?.getAttribute('href') || '';
  }

  set activeTabs(tabs) {
    if (tabs.length) {
      this._activeTabs = tabs;
      for (let tab of tabs) {
        tab.setAttribute('folder-active', 'true');
      }
    } else {
      const folders = new Map();
      for (let tab of this._activeTabs) {
        const group = tab?.group?.hasAttribute('split-view-group') ? tab?.group?.group : tab?.group;
        if (!folders.has(group?.id)) {
          folders.set(group?.id, group?.activeGroups?.at(-1));
        }
        let activeGroup = folders.get(group?.id);
        if (!activeGroup) {
          tab.removeAttribute('folder-active');
          tab.style.removeProperty('--zen-folder-indent');
        }
      }
      this._activeTabs = [];
      folders.clear();
    }
  }

  get activeTabs() {
    return this._activeTabs;
  }

  get resetButton() {
    return this.labelElement.parentElement.querySelector('.tab-reset-button');
  }

  unloadAllTabs(event) {
    this.#unloadAllActiveTabs(event, /* noClose */ true);
  }

  async #unloadAllActiveTabs(event, noClose = false) {
    await gZenPinnedTabManager.onCloseTabShortcut(event, this.tabs, {
      noClose,
      alwaysUnload: true,
      folderToUnload: this,
    });
    this.activeTabs = [];
  }

  on_click(event) {
    if (event.target === this.resetButton) {
      event.stopPropagation();
      this.unloadAllTabs(event);
      return;
    }
    super.on_click(event);
  }

  /**
   * Get the root most collapsed folder in the tree.
   * @returns {ZenFolder|null} The root most collapsed folder, or null if none are collapsed.
   */
  get rootMostCollapsedFolder() {
    let current = this;
    let rootMost = null;
    do {
      if (current.collapsed) {
        rootMost = current;
      }
      current = current.group;
    } while (current);
    return rootMost;
  }
}

customElements.define('zen-folder', ZenFolder);
